#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

// RDB 文件的一些常量
#define RDB_MAGIC "REDIS"
#define RDB_VERSION "0011"
#define RDB_6BITLEN 0
#define RDB_16BITLEN 1
#define RDB_32BITLEN 2
#define RDB_ENCVAL 3

// RDB 类型
#define RDB_TYPE_STRING 0
#define RDB_TYPE_LIST 1
#define RDB_TYPE_SET 2
#define RDB_TYPE_ZSET 3
#define RDB_TYPE_HASH 4


struct rdb_header {
  char magic[5];    // "REDIS"
  char version[4];  // "0011"
};

// 从文件读取指定长度的数据
size_t rdb_read(FILE *fp, void *buf, size_t len) {
  return fread(buf, 1, len, fp);
}

// 添加字节序处理函数
uint32_t load_uint32(FILE *fp) {
  uint32_t value;
  uint8_t buf[4];

  if (rdb_read(fp, buf, 4) != 4) {
    return 0;
  }

  // 从小端序字节构建 uint32
  value = ((uint32_t)buf[0]) | ((uint32_t)buf[1] << 8) |
          ((uint32_t)buf[2] << 16) | ((uint32_t)buf[3] << 24);

  return value;
}

// 简单的键值对结构
typedef struct {
    char *key;
    char *value;
} KeyValue;

// 写入 RDB 文件头
void write_rdb_header(FILE *file) {
    char magic[6] = {0}; // magic 字符串以空字符结尾，所以需要6个字节的空间
    memcpy(magic, RDB_MAGIC, 5); // 复制 magic 字符串，不包括空字符
    fwrite(magic, 1, 5, file); // 写入 magic 字符串
    char version[5] = {0};
    memcpy(version, RDB_VERSION, 4);
    fwrite(version, 1, 4, file); // 写入版本号
    uint8_t dbindex = 0xFE;
    fwrite(&dbindex, sizeof(uint8_t), 1, file);
    uint8_t index = 0; // 假设只有一个数据库
    fwrite(&index, sizeof(uint8_t), 1, file);
}

// 写入字符串对象到 RDB 文件
void write_string_object(FILE *file, const char *str, size_t len) {
    uint32_t strlen_rdb = len;
    fwrite(&strlen_rdb, sizeof(uint32_t), 1, file);
    fwrite(str, sizeof(char), len, file);
}

// 写入键值对到 RDB 文件
void write_key_value(FILE *file, KeyValue *kv) {
    write_string_object(file, kv->key, strlen(kv->key));
    write_string_object(file, kv->value, strlen(kv->value));
}

// 更新 RDB 文件的 EOF 和校验和
void write_rdb_footer(FILE *file) {
    uint64_t eof=0xFF; // EOF 标记，稍后写入
    fwrite(&eof, sizeof(uint64_t), 1, file);
    uint32_t checksum = 0; // 校验和，稍后计算并写入
    fwrite(&checksum, sizeof(uint32_t), 1, file);
}

void write_test() {
 // 示例键值对
    KeyValue kv_list[] = {
        {"key1", "value1"},
        {"key2", "value2"},
        {"key3", "value3"}
    };
    size_t kv_count = sizeof(kv_list) / sizeof(kv_list[0]);

    // 打开 RDB 文件
    FILE *rdb_file = fopen("dump.rdb", "wb");
    if (!rdb_file) {
        perror("Failed to open RDB file");
        return;
    }

    // 写入 RDB 文件头
    write_rdb_header(rdb_file);

    uint8_t entrysize = 0xFB;
    fwrite(&entrysize, sizeof(uint8_t), 1, rdb_file);
    uint8_t ucount = kv_count;
    fwrite(&ucount, sizeof(uint8_t), 1, rdb_file);

    // 写入键值对
    for (size_t i = 0; i < kv_count; i++) {
        uint8_t type = RDB_TYPE_STRING;
        fwrite(&type, sizeof(uint8_t), 1, rdb_file);
        write_key_value(rdb_file, &kv_list[i]);
    }

    write_rdb_footer(rdb_file);

    // 关闭文件
    fclose(rdb_file);

    printf("RDB file written successfully.\n");
}


void read_test() {
  // 打开 RDB 文件
  FILE *file = fopen("dump.rdb", "rb");
  if (!file) {
      perror("Failed to open RDB file");
      return;
  }
  // 读取 RDB 文件头
  struct rdb_header header;
  char magic[6] = {0};
  char version[5] = {0};
  fread(magic, 5, 1, file);
  fread(version, 4, 1, file);
  printf("Magic: %s\n", magic);
  printf("Version: %s\n", version);
  uint8_t dbindex = 0;
  fread(&dbindex, sizeof(uint8_t), 1, file);
  uint8_t index = 0;
  fread(&index, sizeof(uint8_t), 1, file);
  uint8_t oxfb = 0;
  fread(&oxfb, sizeof(uint8_t), 1, file);
  uint8_t entrysize = 0;
  fread(&entrysize, sizeof(uint8_t), 1, file);
  for (int i = 0; i < entrysize; i++) {
    uint8_t type = 0;
    fread(&type, sizeof(uint8_t), 1, file);
    uint32_t keylen = load_uint32(file);
    char skey[keylen];
    fread(skey, 1, keylen, file);
    uint32_t valuelen = load_uint32(file);
    char value[valuelen];
    fread(value, 1, valuelen, file);
    printf("Key: %s, Value: %s\n", skey, value);
  }
  uint8_t eof = 0;
  fread(&eof, sizeof(uint8_t), 1, file);
  if (eof != 0xFF) {
    printf("EOF is not 0xFF\n");
    return;
  }
  uint32_t checksum = load_uint32(file);
  printf("Checksum: %u\n", checksum);

}

int main() {
    write_test();
    read_test();
    return EXIT_SUCCESS;
}